package main

import (
	"fmt"
	"unsafe"
)

// ****** 结构体是 值类型 ******
// go语言中的struct更像python的namedtuple

/**
1. 如果结构体的数据类型为：指针 slice map时，它们的零值都是nil，需要先make才能使用
2. 结构体的字段在内存中是连续的
*/

// cat结构体
type Cat struct {
	name    string
	color   string
	age     int
	ptr     *int
	slice   []int
	mapData map[string]string
}

// person结构体
type person struct {
	name, city string
	age        int8
}

// person构造函数 ---> 工厂模式(减少程序拷贝开销)
func NewPerson(name, city string, age int8) *person {
	return &person{
		name: name,
		city: city,
		age:  age,
	}
}

// person使用指针接受者
func (p *person) SetAge(newAge int8) {
	p.age = newAge
}

// person做梦的方法
func (p person) Dream() {
	fmt.Printf("%s的梦想是学好Go语言!\n", p.name)
}

func main() {
	var p person
	p.name = "wangwu"
	p.city = "上海"
	p.age = 24
	fmt.Println(p) // {wangwu 上海 24}
	// p.SetAge(90)
	// p.SetAge(100)
	// p.SetAge(120)

	// 因为person是一个指针，所以访问属性的标准写法: (*p12).name = "p12_value"
	// go的设计者为了使程序员方便使用，也可以使用p12.name = "p12_value"
	// 原因: 底层会对p12.name = "p12_value"进行处理---取值运算
	var p12 *person = &person{}
	// (*p12).name = "p12_value"
	p12.name = "p12_value"
	p12.age = 100
	fmt.Println("p12:", p12) // p12: &{p12_value  100}

	var p13 person = *p12
	p13.name = "p13_value"
	fmt.Println("p13:", p13)                                       // p13: {p13_value  100}
	fmt.Printf("p12.name: %v, p13.name: %v\n", p12.name, p13.name) // p12.name: p12_value, p13.name: p13_value

	// 1 匿名结构体
	var user struct {
		Name string
		Age  int
	}
	user.Name = "小王子"
	user.Age = 18
	fmt.Printf("%#v\n", user) // {Name:"小王子", Age:18}

	// 2 值类型接收者
	p1 := NewPerson("小王子", "北京", 25)
	p1.Dream()          // 小王子的梦想是学好Go语言！
	fmt.Println(p1.age) // 25

	// 3 数据类型
	var cat Cat
	fmt.Println("cat:", cat) // {  0 <nil> [] map[]}
	if cat.name == "" {
		fmt.Println("name is nil")
	}
	if cat.color == "" {
		fmt.Println("color is nil")
	}
	if cat.age == 0 {
		fmt.Println("age is nil")
	}
	if cat.ptr == nil {
		fmt.Println("ptr is nil")
	}

	cat.slice = make([]int, 10)
	cat.slice[0] = 100
	fmt.Println("cat:", cat) // {  0 <nil> [100 0 0 0 0 0 0 0 0 0] map[]}

	cat.mapData = make(map[string]string)
	cat.mapData["小白"] = "白色"
	fmt.Println("cat:", cat) // {  0 <nil> [100 0 0 0 0 0 0 0 0 0] map[小白:白色]}

	// 4 结构体嵌套-1
	var cp = CatTypes{
		Name: "大众",
		Age:  100,
		AoDiDriver: AoDiDriver{
			Color: "白色",
			Brand: "奥迪",
		},
		BaoMaDriver: BaoMaDriver{
			Color: "黑色",
			Brand: "宝马",
		},
	}
	cp.sayHello() // name and age: 大众 100; 奥迪汽车的颜色: 白色; 宝马汽车的品牌: 宝马

	// 5 结构体嵌套-2
	pupil := &Pupil{
		Student: Student{
			Name: "tom",
			Age:  10,
		},
	}
	pupil.testing() // 小学生正在考试
	pupil.setScore(68.8)
	pupil.getInfo() // name=tom, age=10, score=68.8
	pup_res := pupil.getSum(10, 20)
	fmt.Println("pup_res:", pup_res) // 30

	grad := &Graduate{
		Student: Student{
			Name: "mary",
			Age:  28,
		},
	}
	grad.testing() // 大学生正在考试
	grad.setScore(98.2)
	grad.getInfo()                    // name=mary, age=28, score=98.2
	grad_res := grad.getSum(100, 120) // 220
	fmt.Println("grad_res:", grad_res)

	// 6. 匿名结构体
	NiMingFunc()

	// 7. struct with json
	StructWithJson()

	// 8 初始化实例对象
	ZeroValueType()

	// 9 struct的长度
	var house = House{
		"汤臣一品",
		90,
		18,
	}
	// string默认位16个字节，int占8个字节
	fmt.Println("house的长度:", unsafe.Sizeof(house)) // 32
}
